/*
 * main.c
 *
 *  Created on: 2010-12-16
 *      Author: minfang
 */

#include <avr/io.h>
#include <avr/iom16.h>

#include <avr/interrupt.h>
#include <util/delay.h>

#include "port_defined.h"
#include "status_define.h"
#include "led_function.h"
#include "keyboard.h"
#include "light_control.h"
#include "speaker.h"
#include "temp_device.h"
#include "eeprom.h"
/// from left to right


#define HALF_SEONCD_COUNTER 976

#define AUTO_GO_BACK_SHOW_TEMP_TIME 48

// HOW LONG BEtween two warning

#define WARNING_TIME_CAP 120*5

//2 minuts
#define WANING_TIME 120*2

#define SHORT_HOLD_KEY_TIME 2   // hold key for 2 seconds , the status of setting will be change
#define LONG_HOLD_KEY_TIME 6  // hold key for 4 seconds ,
#define MUSIC_OUTPUT DDRA |= (1 << MUSIC_PORT)
#define MUSIC_PLAY PORTA |= (1 << MUSIC_PORT)
#define MUSIC_STOP PORTA &= ~(1 << MUSIC_PORT)

volatile unsigned int timer_counter;
volatile unsigned int timer_interrupt_counter;

volatile char current_mode_status;

volatile char setup_status;

volatile char blink_site;

volatile char c1, c2, c3;

volatile char pre_key_status;
volatile char key_hold_timer;
volatile unsigned int blink_counter;
volatile char send_key_message_flag;

volatile char blink_status;
volatile char temp_device_status;

volatile char auto_go_back_temp_show_timer;

volatile char save_ee_flag = 0;

volatile char setting_auto_back_timer;

volatile char warning_status;
volatile int warning_timer;

volatile char day_night_flag = STATUS_DAY_NIGHT_DAY;

void set_timer() {
	TCNT1H = 0Xff;
	TCNT1L = 0Xf0;

	// time out = 8000000/128/16= 1/3906.25 second

}
void convert_2_show(char number) {
	c1 = number / 10;
	c2 = number % 10;
}

void convert_2_show3(int number) {
	c1 = number / 100;
	number %= 100;
	c2 = number / 10;
	c3 = number % 10;
}

void show_temp() {

	if (current_mode_status != STATUS_SHOW_TEMP)
		return;
	convert_2_show3(current_temp);
	led_show(c1, 0, c2, 1, c3, 0);

}
void show_setting() {

	if (current_mode_status != STATUS_SHOW_SETTING)
		return;
	convert_2_show3(current_temp);
	led_show(c1, 0, c2, 1, c3, 0);

}

void set_mode_to_show_temp() {
	current_mode_status = STATUS_SHOW_TEMP;
	show_temp();
}

void from_setting_return_to_normal() {

	save_ee_flag = 1;
	set_mode_to_show_temp();
}

void long_hold_key() {

	if (current_mode_status == STATUS_SHOW_TEMP) {

		// convert to STATUS_SHOW_SETTING
		current_mode_status = STATUS_SHOW_SETTING;
		setup_status = STATUS_SETUP_START_ALARM_LOW_TEMP;
		blink_site = STATUS_BLINK_SITE1;
		//
		setting_auto_back_timer = 0;

	} else if (current_mode_status == STATUS_SHOW_LIGHT) {
		// turn on alarm for testing
		speaker_on();
	} else {
		//STATUS_SHOW_SETTING
		from_setting_return_to_normal();

	}

}

void short_hold_key() {

	if (current_mode_status == STATUS_SHOW_SETTING) {

		setting_auto_back_timer = 0;
		if (blink_site == STATUS_BLINK_SITE1) {
			blink_site = STATUS_BLINK_SITE2;
		} else {
			blink_site = STATUS_BLINK_SITE1;
			setup_status++;
			if (setup_status > STATUS_SETUP_LAST) {
				setup_status = STATUS_SETUP_FIRST;
			}
		}

	}

}

void show_light() {
	if (current_mode_status != STATUS_SHOW_LIGHT)
		return;
	convert_2_show3(current_light);
	led_show(c1, 0, c2, 0, c3, 0);
}

void setting_value_plus(volatile char * v) {
	if (blink_site == STATUS_BLINK_SITE1) {
		*v += 10;
		if (*v >= 100) {
			*v %= 100;
		}
	} else {

		char t1 = *v % 10;
		if (t1 < 9) {
			*v += 1;
		} else {
			*v -= 9;
		}

	}
}
void blink_process() {
	if (current_mode_status != STATUS_SHOW_SETTING)
		return;

	switch (setup_status) {
	case STATUS_SETUP_START_ALARM_LOW_TEMP:
		convert_2_show(temp_alarm_low_start);
		break;
	case STATUS_SETUP_STOP_ALARM_LOW_TEMP:
		convert_2_show(temp_alarm_low_stop);
		break;
	case STATUS_SETUP_START_ALARM_HIGH_TEMP:
		convert_2_show(temp_alarm_high_start);
		break;
	case STATUS_SETUP_STOP_ALARM_HIGH_TEMP:
		convert_2_show(temp_alarm_high_stop);
		break;
	case STATUS_SETUP_START_NIGHT:
		convert_2_show(light_night);

		break;
	case STATUS_SETUP_START_DAY:
		convert_2_show(light_day);
		break;

	case STATUS_SETUP_NO_WARN_CAP_TIME:
		convert_2_show(no_warn_cap_time);
		break;

	default:
		c1 = 16;
		c2 = 16;

	}

	if (blink_status) {

		blink_status = 0;

		led_show(c1, 0, c2, 0, setup_status, 0);

	} else {
		blink_status = 1;
		if (blink_site == STATUS_BLINK_SITE1) {
			c1 = 16;
		} else {
			c2 = 16;

		}
		led_show(c1, 0, c2, 0, setup_status, 0);
	}

}

void click_key() {

	speaker_off();

	if (warning_status == STATUS_WARNING_LOW_WARNING || warning_status
			== STATUS_WARNING_HIGH_WARNING) {
		/// stop warning , and set the cap timer
		warning_status = STATUS_WARNING_WARNING_CAP;
		warning_timer = 0;
	}
	if (current_mode_status == STATUS_SHOW_TEMP) {
		current_mode_status = STATUS_SHOW_LIGHT;
		auto_go_back_temp_show_timer = 0;
		show_light();

	} else if (current_mode_status == STATUS_SHOW_LIGHT) {
		set_mode_to_show_temp();

	} else {
		setting_auto_back_timer = 0;

		/// current setting value ++
		switch (setup_status) {
		case STATUS_SETUP_START_ALARM_LOW_TEMP:
			setting_value_plus(&temp_alarm_low_start);
			break;
		case STATUS_SETUP_STOP_ALARM_LOW_TEMP:
			setting_value_plus(&temp_alarm_low_stop);
			break;
		case STATUS_SETUP_START_ALARM_HIGH_TEMP:
			setting_value_plus(&temp_alarm_high_start);
			break;
		case STATUS_SETUP_STOP_ALARM_HIGH_TEMP:
			setting_value_plus(&temp_alarm_high_stop);
			break;
		case STATUS_SETUP_START_NIGHT:
			setting_value_plus(&light_night);

			break;
		case STATUS_SETUP_START_DAY:
			setting_value_plus(&light_day);
			break;

		case STATUS_SETUP_NO_WARN_CAP_TIME:
			setting_value_plus(&no_warn_cap_time);
			break;

		default:
			break;
		}

		blink_status = 1;
		blink_counter = 0;
		blink_process();

	}

}

void key_process() {
	char current_status = KEY_R;
	if (current_status == pre_key_status) {
		if (current_status == STATUS_KEYBOARD_RELEASE)
			return;

		else {
			if (key_hold_timer > LONG_HOLD_KEY_TIME) {
				if (send_key_message_flag != STATUS_KEY_MESSAGE_LONG) {
					send_key_message_flag = STATUS_KEY_MESSAGE_LONG;
					long_hold_key();
				}

			} else if (key_hold_timer > SHORT_HOLD_KEY_TIME) {
				if (send_key_message_flag != STATUS_KEY_MESSAGE_SHORT) {
					send_key_message_flag = STATUS_KEY_MESSAGE_SHORT;
					short_hold_key();
				}
			}
		}
	}

	else if (current_status != STATUS_KEYBOARD_RELEASE) {

		key_hold_timer = 0;
		send_key_message_flag = STATUS_KEY_MESSAGE_NO;
	}

	else {
		if (key_hold_timer < STATUS_KEY_MESSAGE_SHORT) {
			click_key();
		}
	}
	pre_key_status = current_status;
}

void half_second_trigger() {
	asm("WDR;\n");
	timer_counter++;

	if (((timer_counter & 0x001f) == 0) && temp_device_status
			== STATUS_TEMP_DEVICE_UNUSING) {

		/// every 16 second, fetch temp value
		temp_device_status = STATUS_TEMP_DEVICE_USING;
	}

	if ((timer_counter & 0x0001) == 0) {
		//every 4 second ,fetch light
		fetch_light();
	}

	if (current_mode_status == STATUS_SHOW_LIGHT) {
		auto_go_back_temp_show_timer++;
		if (auto_go_back_temp_show_timer > AUTO_GO_BACK_SHOW_TEMP_TIME) {
			set_mode_to_show_temp();
		}
	}

	if (current_mode_status == STATUS_SHOW_SETTING) {
		setting_auto_back_timer++;
		if (setting_auto_back_timer > AUTO_GO_BACK_SHOW_TEMP_TIME) {
			from_setting_return_to_normal();
		}
	}

	if (warning_status == STATUS_WARNING_LOW_WARNING || warning_status
			== STATUS_WARNING_HIGH_WARNING) {
		warning_timer++;
		if (warning_timer > WANING_TIME) {

			//auto stop warning
			warning_status = STATUS_WARNING_WARNING_CAP;
			warning_timer = 0;
			speaker_off();
		}
	}

	else if (warning_status == STATUS_WARNING_WARNING_CAP) {

		if (warning_timer > WARNING_TIME_CAP) {

			//out of cap time
			warning_status = STATUS_WARNING_NO_WARNING;
			warning_timer = 0;

		}
	}

}

ISR( TIMER1_OVF_vect)

{

	//reset the interrupt
	set_timer();

	led_process();

	key_process();

	blink_counter++;
	if (blink_counter > HALF_SEONCD_COUNTER) {
		key_hold_timer++;
		blink_counter = 0;
		blink_process();
	}

	timer_interrupt_counter++;
	if (timer_interrupt_counter > HALF_SEONCD_COUNTER) {
		timer_interrupt_counter = 0;
		half_second_trigger();

	}

}

void timer_init() {
	TCCR1B = 0X04; // 256 clock
	TIMSK |= 1 << 2; // time1 interrupt enable

	timer_counter = 0;
}

void sleep() {
	asm("sleep;\n");
}
void test_light() {

	if (day_night_flag == STATUS_DAY_NIGHT_DAY) {
		if (current_light > light_night * 10) {
			day_night_flag = STATUS_DAY_NIGHT_NIGHT;
		}
	} else {
		if (current_light < light_day * 10) {
			day_night_flag = STATUS_DAY_NIGHT_DAY;

			if (warning_status == STATUS_WARNING_LOW_WARNING || warning_status
					== STATUS_WARNING_HIGH_WARNING) {
				/// stop warning , and set the cap timer
				warning_status = STATUS_WARNING_WARNING_CAP;
				warning_timer = 0;
				speaker_off();
			}

		}

	}

}
void test_temp() {
	if (warning_status == STATUS_WARNING_NO_WARNING && day_night_flag
			== STATUS_DAY_NIGHT_NIGHT) {
		if (current_temp < temp_alarm_low_start * 10) {
			/// low temp warning
			warning_status = STATUS_WARNING_LOW_WARNING;
			speaker_on();
			warning_timer = 0;
		} else if (current_temp > temp_alarm_high_start * 10) {
			// high temp warning
			warning_status = STATUS_WARNING_HIGH_WARNING;
			speaker_on();
			warning_timer = 0;
		}

	}

	else if (warning_status == STATUS_WARNING_LOW_WARNING) {
		if (current_temp > temp_alarm_low_stop * 10) {
			/// stop low temp warning
			warning_status = STATUS_WARNING_NO_WARNING;
			speaker_off();
		}

	}

	else if (warning_status == STATUS_WARNING_HIGH_WARNING) {
		if (current_temp < temp_alarm_high_stop * 10) {
			// stop high temp warning
			warning_status = STATUS_WARNING_NO_WARNING;
			speaker_off();
		}
	}

}

int main(void) {

	/////

	led_init();

	key_init();

	light_init();

	speaker_init();

	temp_device_init();

	//load_all();

	timer_init();

	sei();
	/* enable global interrupts */

	timer_interrupt_counter = 0;
	timer_counter = 0;
	temp_device_status = STATUS_TEMP_DEVICE_USING;
	pre_key_status = STATUS_KEYBOARD_RELEASE;

	warning_status = STATUS_WARNING_NO_WARNING;

	current_mode_status = STATUS_SHOW_TEMP;


	// turn on the watch dog
	// 2.1 s to reset
	WDTCR = (1 <<WDTOE) | (1<<WDE) | (1<<WDP2) |(1<<WDP1)|(1<<WDP0);
	set_timer();

	while (1) {

		sleep();

		if (save_ee_flag) {
			save_ee_flag = 0;
			save_all();
		}

		if (temp_device_status == STATUS_TEMP_DEVICE_USING) {
			fetch_temp();
			test_temp();
			temp_device_status = STATUS_TEMP_DEVICE_UNUSING;
		}

	}

}
